Datenfelder

1. Begriff

Das Hilfethema Datentypen unterscheidet einfache Datentypen wie Ganzzahlen, Gleitkommazahlen und Wahrheitswerte sowie zusammengesetzte Datentypen, die sich aus mehreren Werten einfacher Datentypen zusammensetzen. Die nächsten Kapitel führen die zusammengesetzten Datentypen Satz (engl. Type) und Datenfeld (engl. array) ein. 

Ein Satz ist eine Struktur aus gleichen oder verschiedenen Datentypen. Der folgende Satz setzt sich zum Beispiel aus verschiedenen numerischen und symbolischen Elementen zusammen. Man nennt deshalb einen solchen Datentyp heterogen zusammengesetzt.

137221
Fritz Meier
Bahnhofstrasse 25
9000 St. Gallen
071/223 90 85
01.8.98

Die entsprechende Vereinbarung eines Datentyps Mitarbeiter lautet:

Type Mitarbeiter
  Personalnummer As Integer
  Name As String
  Strasse As String
  Ort As String
  Telefon As Long
  Einstellungsdatum As Date
End Type

Heterorgene Datentypen spielen in den Zeilen von Datenbank-Tabellen eine wichtige Rolle. Im folgenden beschäftigen wir uns nur mit einem homogen zusammengesetzten Datentyp, dem Datenfeld (engl. array). Eine Konstante des Typs Datenfeld besteht aus beliebig vielen Werten des gleichen Datentyps. Im folgenden Beispiel besteht die Datenfeld-Konstante Punktetotale aus den Punktetotalen von fünf Fussballmannschaften:

22

16

28

6

32

Oft setzt man die Elemente einer Datenfeld-Konstante zwischen eckige Klammern: [22, 16, 28, 6, 32]. Eine Datenfeld-Variable ist eine Folge von Zellen des gleichen Datentyps, auf die unter dem gleichen Namen, aber verschiedenen Indizes - zum Beispiel 1 bis 5 - zugegriffen werden kann. Meist ändert sich die Grösse eines Datenfelds während des Programms nicht. Wir definieren deshalb für jedes Datenfeld eine untere und eine obere Grenze, welche den Wertebereich des Index begrenzen:

Name Läufer(1) Läufer(2) Läufer(3) Läufer(4) Läufer(5)
Inhalt “Anna” “Bruno” “Dora” “Franz” “Karin”

Name der Datenfeldvariable: Läufer
Grösse (Dimension): 5
Untergrenze: 1
Obergrenze: 5

Der Datenfeldname und der Index kennzeichnen jedes Element eindeutig. Läufer(2) bezeichnet zum Beispiel den zweiten Läufer. Jede Zelle des Datenfelds gehört dem gleichen Datentyp an. Der Array Läufer kann zum Beispiel nur Werte des Datentyps String enthalten. Ein Datenfeld hat den Vorteil, dass es sowohl ganz als auch elementweise verarbeitet werden kann. Die Indizierung erlaubt ausserdem die einfache Verarbeitung aller Elemente in einer Schleife.

Das Datenfeld Läufer wird wie folgt vereinbart:

Dim Läufer ( 1 To 5 ) As String.

Allgemeiner lautet die Syntax einer Datenfeld-Vereinbarung:

Dim <Datenfeldname> ( <Untergrenze> To <Obergrenze> ) As <Datentyp>.

Statt Dim kann auch Private oder Public stehen (siehe Fallbeispiel Gewinnverteilung). Der Zugriff auf ein Datenfeld ist einfach. Die folgende Anweisung liest den Inhalt der fünften Zelle von Läufer und weist ihn der Variablen Tmp zu:

Dim Tmp As String
Tmp = Läufer(5).

Ebenso einfach ist die Änderung eines Zellinhalts. Die folgende Anweisung ersetzt den Inhalt der fünften Zelle durch “Pauline”:

Läufer(5) = "Pauline".

Die Indizierung erlaubt die einfache Zusammenfassung mehrerer Lese- oder Schreiboperationen: Die folgende Zählschleife weist zum Beispiel allen Zellen den Inhalt “Fritz” zu:

Dim Läufer(1 To 5) As String
Dim i As Integer
For i = 1 To 5
  Läufer(i) = “Fritz”
Next i

Die Zählschleife eignet sich besonders gut für das Durchlaufen eines Datenfelds. Verglichen mit der bereits eingeführten Schleife mit Ausführungsbedingung (While) erspart sie der Programmiererin die explizite Initialisierung, Prüfung und Erhöhung einer Indexvariable.

Die Syntax der Zählschleife lässt sich verallgemeinern:

Dim Index As Integer

For Index = <Untergrenze> To <Obergrenze>
  <Anweisungen>
Next

2. Suchverfahren

Wer im Telefonbuch sucht, kann drei Strategien einsetzen:

Die folgende Tabelle fasst die drei Suchstrategien zusammen und nennt ihre Vor- und Nachteile. Eine schnelle Suchstrategie stellt grössere Anforderungen an die Organisation der Daten und an das Vorwissen des Benutzers:

Suche Suchbeispiel Bedingung Effizienz
direkt Seite 143 Seiten nummeriert sehr schnell
sequentiell 061 / 422 13 07 keine langsam
binär Bohnenblust Fritz Einträge sortiert schnell

Ein konventionelles Telefonbuch ist gleich wie ein Datenfeld organisiert. Eine Seite entspricht einer Zelle, und eine Seitennummer entspricht dem Zellenindex. Wir bilden deshalb die drei Suchstrategien auf unser Datenfeld Läufer ab.

1. Direkte Suche

Direkt suchen heisst mit einem gegebenen Index ein Feldelement bestimmen. Wenn zum Beispiel nach dem Namen des dritten Läufers gefragt wird, so lautet die Antwort:

Name = Läufer(3).

2. Sequentielle Suche

Sequentiell suchen heisst hintereinander jedes Element mit einem Suchwert vergleichen, bis die Suche Erfolg hat. Wenn zum Beispiel nach der Nummer der Läuferin Dora gefragt wird, so lautet die Antwort: Vergleiche der Reihe nach alle Elemente, bis die Nummer gefunden wird:

For Nummer = 1 To 5
  If Läufer(Nummer) = “Dora” Then
    MsgBox “Nummer von Dora = ” & Nummer
    Exit For
  End If
Next Nummer

Die Zählschleife geht solange von Zelle zu Zelle, bis der Inhalt des laufenden Elements mit “Dora” übereinstimmt. Dann wird in einem Ausgabefeld “Nummer von Dora =” und die gefundene Nummer ausgegeben (& verknüpft zwei Zeichenketten und konvertiert dabei die Zahl Nummer automatisch in einen String). Weil die Suche nun beendet ist, verlässt die Anweisung Exit For die Zählschleife. Damit lässt sich die Syntax der Zählschleife wie folgt erweitern:

Dim Index As Integer

For Index = <Untergrenze> To <Obergrenze>
    <Anweisungen>
    [Exit For]
    <Anweisungen>]
Next Index

Exit For steht in eckigen Klammern, weil es weggelassen werden kann, falls die Schleife nicht mitten im Schleifenkörper verlassen werden muss.

3. Binäre Suche

Wer im Telefonbuch sucht, ...

Eine solche Suche heisst Binärsuche. Sie halbiert ein sortiertes Datenfeld solange, bis das gesuchte Element gefunden wird. Bevor wir eine VBA-Prozedur implementieren, beschreiben wir die Binärsuche entwurfssprachlich. Wir stellen dazu den Suchalgorithmus an unserem Läuferbeispiel und dem Beispielaufruf sucheBinär(1, 7, 'Paul', 4) dar. Das erste Argument enthält die Untergrenze 1 des zu durchsuchenden Feldbereichs. Beim ersten Aufruf von sucheBinär ist der zu durchsuchende Feldbereich identisch mit dem ganzen Datenfeld Läufer. Spätere Aufrufe durchsuchen nur noch einen Teil des Datenfelds. Das zweite Argument nennt die Obergrenze des Feldbereichs - beim ersten Aufruf ist dies 7. Das dritte Argument entspricht dem Suchwert - in unserem Beispiel “Paul” -, und das vierte Argument nennt die Mitte des Feldbereichs. Vor dem ersten Aufruf ist die Mitte (1 + 7) / 2 = 4. Spätere Aufrufe gehen von einer neu berechneten Mitte aus.

Die folgende Tabelle erweitert unser Läuferbeispiel um die Zellen 6 und 7 und erlaubt in den letzten drei Zeilen die Verfolgung der binären Suche:

Nr

1

2

3

4

5

6

7

Läufer(Nr)

ANNA

BRUNO

DORA

FRANZ

KARIN

NORA

PAUL

1. Schritt

L

M

R

2. Schritt

L

M

R

3. Schritt

L, M, R

Links Index der Untergrenze des betrachteten Feldbereichs
Mitte Index der Mitte des betrachteten Feldbereichs
Rechts Index der Obergrenze des betrachteten Feldbereichs

Gesucht sei die Startnummer des Läufers Paul. Aus der Tabelle erkennen wir sofort als Suchergebnis 7. Weil der Computer nicht über die menschliche Fähigkeit des unmittelbaren Erkennens von Mustern verfügt, müssen wir ihm den Algorithmus der Binärsuche schrittweise beibringen. Im ersten Schritt ermittelt der Algorithmus aus den Positionsindizes Links und Rechts den Index Mitte. Weil der Läufer auf der Position Mitte (Franz) nicht gleich Paul ist, sucht der Algorithmus weiter. Der Name Paul ist alphabetisch grösser als Franz. Deshalb wird der rechte Teil des Datenfelds weiter untersucht: Die neue Mitte berechnet sich wieder aus Links und Rechts. Die Läufern an der neuen Position Mitte heisst Nora und ist ebenfalls kleiner als Paul. Weil jetzt der rechte Teil des Datenfelds nur noch aus einer einzigen Zelle besteht, fallen die Positionen Links, Rechts und Mitte zusammen, und der Läufer an der Position Mitte entspricht dem Suchwert Paul.

Dieser verbalen Beschreibung der Binärsuche entspricht der folgende entwurfssprachliche Algorithmus. Die obige Beispielsuche entspricht dem Aufruf sucheBinär(1, 7, 'Paul', 4).

sucheBinär(links,rechts, Name, Mitte)
  Mitte = (links + rechts) \ 2 ‘ ganzzahlige Division
  Falls links <= Mitte dann
    Falls Name = Feld(Mitte) dann
      Name gefunden
    sonst
      Falls Name < Feld(Mitte) dann
        sucheBinär(links, Mitte-1, Name, Mitte)
      sonst Name > Feld(Mitte)
        sucheBinär(Mitte+1, rechts, Name, Mitte)
  sonst Name nicht gefunden

Der Rückwärtsstrich-Operator \ dividiert ganzzahlig. Zum Beispiel ergibt 5 \ 2 statt der Gleitkommazahl 2.5 die Ganzahl 2. Der Algorithmus ruft sich je nach Bedingung mit einer der fett gedruckten Anweisungen selbst wieder auf. Er heisst deshalb rekursiv (von lat. recurrere = zurücklaufen). Die folgende Geschichte veranschaulicht den Grundgedanken der Rekursion:

Ein Hund kam in die Küche und stahl dem Koch ein Ei,
da nahm der Koch den Löffel und schlug den Hund entzwei,
da kamen viele Hunde und gruben ihm ein Grab und setzten ihm einen Grabstein, worauf geschrieben stand:
Ein Hund kam in die Küche und stahl dem Koch ein Ei,
da nahm der Koch den Löffel und schlug den Hund entzwei,
da kamen viele Hunde und gruben ihm ein Grab und setzten ihm einen Grabstein, worauf geschrieben stand:
...

Diese Geschichte ist unendlich rekursiv, weil sie sich selbst aufruft, indem sie auf die vorangehende Geschichte zurückspringt. Die Katze beisst sich also in den Schwanz. Im Gegensatz zur Hundegeschichte ist sucheBinär endlich rekursiv. Ein endlich rekursives Programm ruft sich nur solange auf, bis eine Endbedingung erfüllt wird. In unserem Fall soll der Name an der Position Mitte gleich dem gesuchten Namen ein.

Wenn Sie auf den Startknopf von sucheBinär.xls drücken, erscheint ein Eingabefeld, das Sie auffordert, einen Suchnamen - zum Beispiel NORA - einzugeben (siehe Bild unten). Die gefundene Zellnummer wird dann in einem Ausgabefeld angezeigt. Andernfalls erscheint die Fehlermeldung “Kein Element mit diesem Wert”. Ereignisprozedur der Schaltfläche ‘Suche binär’ ist die Subroutine sucheBinär() (siehe Programmausschnitt unten).

Damit das Datenfeld Läufer sowohl in der Subroutine sucheBinär() als auch in der aufgerufenden Funktion gefunden gelesen werden kann, vereinbaren wir Läufer als globale Variable:

Private Läufer(1 To 7) As String ‘globale Variable

Leserlicher und sicherer wäre eine Vereinbarung von Läufer in der Subroutine sucheBinär() und eine Übergabe des Datenfelds an die Funktion gefunden:

If gefunden(Läufer, 1,7, Suchname, Nr) = True Then ...

Datenfelder können aber nur in der neuesten Version von Visual Basic Argumente von Prozeduren sein. Das Datenfeld Läufer vereinbaren wir deshalb als globale Variable (die in allen Prozeduren gelesen und verändert werden könnte):

gefunden(links, rechts, Läufername, Mitte) ist eine boolsche Funktion, welche die entwurfssprachliche Version der Binärsuche implementiert. Sie ergibt True, falls der Suchname im Datenfeld vorkommt. Die Nummer der gefundenen Zelle wird als Argument Mitte zurückgegeben (letzter Programmausschnitt unten).